#include <stdlib.h>
#include <string.h>
#include <signal.h>
#include <semaphore.h>
#include "libCache.h"
#include "CUCA_engine.h"

// Este include es necesario para que el include <memcached/config_parser.h> no tire error de compilación
#include <stdbool.h>
// Aca estan las tools de memcached para levantar la configuración provista por el usuario en los parametros de ejecución
#include <memcached/config_parser.h>

#include "collections/dictionary.h"

/*
 * Aca defino las variables globales
 */

uint8_t administracion;
uint8_t algoritmoEleccion;
uint8_t algoritmoReemplazo;
//TODO MDIFICADO static pthread_mutex_t semCache = PTHREAD_RWLOCK_INITIALIZER;
static pthread_mutex_t mutexCache = PTHREAD_MUTEX_INITIALIZER;
uint8_t frecuenciaDeCompactacion;
size_t cacheSize = 1;
size_t chunkSize = 1;
sem_t semaforoItems;
pthread_mutex_t mutexVectorItem = PTHREAD_MUTEX_INITIALIZER;
t_CUCA_item *vectorItems;
uint8_t cantidadConexiones = 0;

CUCA_diccionario_t diccionario;

/*
 * Estas son las funciones estaticas necesarias para que el engine funcione
 */

static ENGINE_ERROR_CODE CUCA_initialize(ENGINE_HANDLE*, const char* config_str);
static void CUCA_destroy(ENGINE_HANDLE*, const bool force);

static ENGINE_ERROR_CODE CUCA_allocate(ENGINE_HANDLE*, const void* cookie, item **item, const void* key, const size_t nkey, const size_t nbytes, const int flags, const rel_time_t exptime);
static bool CUCA_get_item_info(ENGINE_HANDLE *, const void *cookie, const item* item, item_info *item_info);
static ENGINE_ERROR_CODE CUCA_store(ENGINE_HANDLE*, const void *cookie, item* item, uint64_t *cas, ENGINE_STORE_OPERATION operation, uint16_t vbucket);
static void CUCA_item_release(ENGINE_HANDLE*, const void *cookie, item* item);
static ENGINE_ERROR_CODE CUCA_get(ENGINE_HANDLE*, const void* cookie, item** item, const void* key, const int nkey, uint16_t vbucket);

static ENGINE_ERROR_CODE CUCA_flush(ENGINE_HANDLE*, const void* cookie, time_t when);

static ENGINE_ERROR_CODE CUCA_item_delete(ENGINE_HANDLE*, const void* cookie, const void* key, const size_t nkey, uint64_t cas, uint16_t vbucket);

/*
 * ************************** Dummy Functions **************************
 *
 * Estas funciones son dummy, son necesarias para que el engine las tengas
 * pero no tienen logica alguna y no seran necesarias implementar
 *
 */

static const engine_info* CUCA_get_info(ENGINE_HANDLE*);
static ENGINE_ERROR_CODE CUCA_get_stats(ENGINE_HANDLE*, const void* cookie, const char* stat_key, int nkey, ADD_STAT add_stat);
static void CUCA_reset_stats(ENGINE_HANDLE*, const void *cookie);
static ENGINE_ERROR_CODE CUCA_unknown_command(ENGINE_HANDLE*, const void* cookie, protocol_binary_request_header *request, ADD_RESPONSE response);
static void CUCA_item_set_cas(ENGINE_HANDLE *, const void *cookie, item* item, uint64_t val);

/*******************chunkSize***************************************************/

/*
 * Esta función es la que va a ser llamada cuando se reciba una signal
 */
void CUCA_dummp(int signal);
/*
 * Esta va a ser la estructura donde voy a ir almacenando la información que me llegue.
 */
t_dictionary *cache;

/*
 * Esta es la función que va a llamar memcached para instanciar nuestro engine
 */MEMCACHED_PUBLIC_API ENGINE_ERROR_CODE create_instance(uint64_t interface, GET_SERVER_API get_server_api, ENGINE_HANDLE **handle) {
	//printf("create_instance. \n");
	/*
	 * Verify that the interface from the server is one we support. Right now
	 * there is only one interface, so we would accept all of them (and it would
	 * be up to the server to refuse us... I'm adding the test here so you
	 * get the picture..
	 */
	/*if (interface == 0) {
	 printf("interface == 0. \n");
	 return ENGINE_ENOTSUP;
	 }*/

	/*
	 * Allocate memory for the engine descriptor. I'm no big fan of using
	 * global variables, because that might create problems later on if
	 * we later on decide to create multiple instances of the same engine.
	 * Better to be on the safe side from day one...
	 */
	//printf("previa a hacer calloc.\n");
	t_CUCA_ng *engine = calloc(1, sizeof(t_CUCA_ng));
	/*	printf("Hice calloc.\n");
	 if (engine == NULL) {
	 printf("engine == null.\n");
	 return ENGINE_ENOMEM;
	 }*/

	/*
	 * We're going to implement the first version of the engine API, so
	 * we need to inform the memcached core what kind of structure it should
	 * expect
	 */
	//printf("Intento cargar los registros de engine.\n");
	engine->engine.interface.interface = 1;

	/*
	 * La API de memcache funciona pasandole a la estructura engine que esta dentro de nuestra
	 * estructura t_CUCA los punteros a las funciónes necesarias.
	 */
	engine->engine.initialize = CUCA_initialize;
	engine->engine.destroy = CUCA_destroy;
	engine->engine.get_info = CUCA_get_info;
	engine->engine.allocate = CUCA_allocate;
	engine->engine.remove = CUCA_item_delete;
	engine->engine.release = CUCA_item_release;
	engine->engine.get = CUCA_get;
	engine->engine.get_stats = CUCA_get_stats;
	engine->engine.reset_stats = CUCA_reset_stats;
	engine->engine.store = CUCA_store;
	engine->engine.flush = CUCA_flush;
	engine->engine.unknown_command = CUCA_unknown_command;
	engine->engine.item_set_cas = CUCA_item_set_cas;
	engine->engine.get_item_info = CUCA_get_item_info;

	//engine->get_server_api = get_server_api;
	//printf("Cargo el ultimo registro del engine.\n");
	/*
	 * memcached solo sabe manejar la estructura ENGINE_HANDLE
	 * el cual es el primer campo de nuestro t_CUCA
	 * El puntero de engine es igual a &engine->engine
	 *
	 * Retornamos nuestro engine a traves de la variable handler
	 */
	//printf("Defino un handle\n");
	*handle = (ENGINE_HANDLE*) engine;
	/* creo la cache de almacenamiento */
	/*	printf("Previo a entrar a _cache_item_destroy\n");
	 void _cache_item_destroy(void *item) {
	 printf("_cache_item_destroy\n");
	 // La variable item es un elemento que esta
	 // dentro del dictionary, el cual es un
	 // t_CUCA_item. Este solo puede ser borrado
	 // si no esta "storeado"

	 ((t_CUCA_item*) item)->stored = false;

	 CUCA_item_release(NULL, NULL, item);
	 }*/
	/*printf("Creo un diccionario.\n");
	 cache = dictionary_create(_cache_item_destroy);
	 printf("Fin de create_instance\n");*/
	return ENGINE_SUCCESS;
}

/*
 * Esta función se llama inmediatamente despues del create_instance y sirbe para inicializar
 * la cache.
 */
static ENGINE_ERROR_CODE CUCA_initialize(ENGINE_HANDLE* handle, const char* config_str) {
	/*
	 * En todas las funciones vamos a recibir un ENGINE_HANDLE* handle, pero en realidad
	 * el puntero handler es nuestra estructura t_CUCA
	 */
	//t_CUCA_ng *engine = (t_CUCA_ng*) handle;
	/*
	 * En la variable config_str nos llega la configuración en el formato:
	 *
	 * 	cache_size=1024;chunk_size=48; etc ...
	 *
	 * La función parse_config recibe este string y una estructura que contiene
	 * las keys que debe saber interpretar, los tipos y los punteros de las variables donde tiene
	 * que almacenar la información.
	 *
	 */
	inicializarPathLogueo("../RC/log.txt");
	mtrace();
	archConfig_t archConfig;
	if (leerArchivoConfiguracion(&archConfig, 1)) {
		return ENGINE_FAILED;
	}
	inicializarNivelLogueo(archConfig.nivelLog);

	cantidadConexiones = archConfig.cantidadConexiones;
	/********************Inicializo el vector*********************************/
	vectorItems = malloc(sizeof(t_CUCA_item) * cantidadConexiones);
	/************************************************************************/

	/****************Inicializo el semáforo*********************/
	sem_init(&semaforoItems, 0, cantidadConexiones);
	/*********************************************************/
	diccionario.cache = malloc(archConfig.tamanio);
	frecuenciaDeCompactacion = archConfig.frecuenciaCompactacion;
	//printf(">Tamaño de la cache: [%d]\n", archConfig.tamanio);
	cacheSize = archConfig.tamanio;
	//printf(">Tamaño de los chunks: [%d]<\n", archConfig.tamanioChunk);
	chunkSize = archConfig.tamanioChunk;
	diccionario.vectorCache = inicializarCache(archConfig.tamanio);
	//printf("cacheSize: [%d]\n", cacheSize);
	//printf("chunkSize: [%d]\n", chunkSize);
	//printf("sizeof(memCache_t): [%d]\n", sizeof(memCache_t));
	//printf(">Tamaño del vector cache: [%d]<\n", sizeof(memCache_t) * (cacheSize / chunkSize));

	if ((mlock(diccionario.cache, archConfig.tamanio) == 0)) {
		//printf("Se hizo el mlock correctamente\n");
	} else {
		perror("mlock");

		//	return ENGINE_FAILED;
	}
	if (strcmp(archConfig.adminMemoria, "Compactacion") == 0) {
		//printf(">>Administracion: Compactación\n");
		administracion = ADMINISTRACION_COMPACTACION;
		if (strcmp(archConfig.algoritmoEleccion, "NextFit") == 0) {
			//printf(">>Eleccion: Next Fit\n");
			algoritmoEleccion = ALGORITMO_NEXT_FIT;
		} else if (strcmp(archConfig.algoritmoEleccion, "WorstFit") == 0) {
			//printf(">>Eleccion: Worst Fit\n");
			algoritmoEleccion = ALGORITMO_WORST_FIT;
		} else {
			return ENGINE_FAILED;
		}
		if (strcmp(archConfig.algoritmoReemplazo, "FIFO") == 0) {
			//printf(">>Reemplazo: Fifo\n");
			algoritmoReemplazo = ALGORITMO_FIFO;
		} else if (strcmp(archConfig.algoritmoReemplazo, "LRU") == 0) {
			//printf(">>Reemplazo: LRU\n");
			algoritmoReemplazo = ALGORITMO_LRU;
		} else {
			return ENGINE_FAILED;
		}
	} else if (strcmp(archConfig.adminMemoria, "Buddy") == 0) {
		//printf(">>Administracion: Buddy system\n");
		administracion = ADMINISTRACION_BUDDY;
		if (strcmp(archConfig.algoritmoEleccion, "NextFit") == 0) {
			//printf(">>Eleccion: Next Fit\n");
			algoritmoEleccion = ALGORITMO_NEXT_FIT;
		} else if (strcmp(archConfig.algoritmoEleccion, "WorstFit") == 0) {
			//printf(">>Eleccion: Worst Fit\n");
			algoritmoEleccion = ALGORITMO_WORST_FIT;
		} else {
			return ENGINE_FAILED;
		}
		if (strcmp(archConfig.algoritmoReemplazo, "FIFO") == 0) {
			//printf(">>Reemplazo: Fifo\n");
			algoritmoReemplazo = ALGORITMO_FIFO;
		} else if (strcmp(archConfig.algoritmoReemplazo, "LRU") == 0) {
			//printf(">>Reemplazo: LRU\n");
			algoritmoReemplazo = ALGORITMO_LRU;
		} else {
			return ENGINE_FAILED;
		}
	} else {
		return ENGINE_FAILED;
	}

	//if (config_str != NULL) {
	//	struct config_item items[] = { { .key = "cache_size", .datatype = DT_SIZE, /*.value.dt_size = &engine->config.cache_max_size */}, { .key = "chunk_size", .datatype = DT_SIZE, /*.value.dt_size = &engine->config.chunk_size */}, { .key = "item_size_max", .datatype = DT_SIZE, /*.value.dt_size = &engine->config.block_size_max*/}, { .key = NULL } };
//
	//	parse_config(config_str, items, NULL);
	//}

	/*
	 * Registro la SIGUSR1. El registro de signals debe ser realizado en la función initialize
	 */
	/*uint8_t i;
	 for(i = 0 ; i< (cacheSize/chunkSize); i++){
	 printf("El codigo buddy [%d] es: [%d]\n", i , diccionario.vectorCache[i].codBuddy);
	 }*/
	signal(SIGUSR1, CUCA_dummp);
	//printf("espero el retorno\n");

	return ENGINE_SUCCESS;
}

/*
 * Esta función es la que se llama cuando el engine es destruido
 */
static void CUCA_destroy(ENGINE_HANDLE* handle, const bool force) {
	cerrarArchivoLog();
	free(diccionario.cache);
	free(diccionario.vectorCache);
	free(vectorItems);
	free(handle);
	muntrace();
}

/*
 * Esto retorna algo de información la cual se muestra en la consola
 */
static const engine_info* CUCA_get_info(ENGINE_HANDLE* handle) {
	//printf("CUCA_get_info.\n");
	static engine_info info = { .description = "CUCA Engine Definitive Version", .num_features = 0, .features = { [0].feature = ENGINE_FEATURE_LRU, [0].description = "Hay soporte de LRU" } };
	return &info;
}

// Esta función es la que se encarga de allocar un item. Este item es la metadata necesaria para almacenar la key
// y el valor. Esta función solo se llama temporalemente antes de hacer, por ejemplo, un store. Luego del store
// el motor llama a la función release. Es por ello que utilizamos un flag "stored" para saber si el elemento
// allocado realmente fue almacenado en la cache o no.
// Puede ocurrir que este mismo se llame 2 veces para la misma operación. Esto es porque el protocolo ASCII de
// memcached hace el envio de la información en 2 partes, una con la key, size y metadata y otra parte con la data en si.
// Por lo que es posible que una vez se llame para hacer un allocamiento temporal del item y luego se llame otra vez, la cual
// si va a ser almacenada.
static ENGINE_ERROR_CODE CUCA_allocate(ENGINE_HANDLE *handler, const void* cookie, item **itemUsado, const void* key, const size_t nkey, const size_t nbytes, const int flags, const rel_time_t exptime) {
	//printf("CUCA_allocate.\n");
	/* ORIGINAL
	 //t_CUCA_item *it = malloc(sizeof(t_CUCA_item));
	 it->flags = flags;
	 it->exptime = exptime;
	 it->nkey = nkey;
	 it->ndata = nbytes;
	 //it->key = malloc(nkey);
	 it->data = malloc(nbytes);
	 it->stored = false;

	 memcpy(it->key, key, nkey);
	 *item = it;
	 */

	/* PRIMERA MODIFICACION*/
	sem_wait(&semaforoItems);
	pthread_mutex_lock(&mutexCache);
	loguearDatosTemplate("DEBUG", time(NULL), "RC", (uint32_t) getpid(), (uint32_t) pthread_self(), NIVEL_LOG_OBLIGATORIO, "Allocate - Key [%s]", key);
	t_CUCA_item* itemAlocado = buscarYOcuparVectorItem();
	*itemUsado = itemAlocado;
	/*return ENGINE_SUCCESS;*/

	/*segunda modificacion*/
	ENGINE_ERROR_CODE retorno;
	//printf(">> Se bloquea la cache para escritura.\n\n");
	if (yaExisteKey((char*) key, nkey, diccionario)) {
		if (algoritmoEleccion == ALGORITMO_WORST_FIT) {
			retorno = buscaYBorraWorstFit(diccionario.vectorCache, (char*) key, nkey);
		} else {
			retorno = buscaYBorraNextFit(diccionario.vectorCache, (char*) key, nkey);
		}
	}
	uint8_t vecesQueBorre = 0;
	//almaceno un valor y lo asocio a su key
	div_t division = div(nbytes, chunkSize);
	uint32_t cantChunks = division.quot;
	if (division.rem > 0) {
		cantChunks++;
	}
	if (cantChunks * chunkSize <= cacheSize) {
		//printf(">> Necesitamos [%d] chunks\n", cantChunks);
		if (administracion == ADMINISTRACION_COMPACTACION) {
			//printf(">> El algoritmo de Administracion de Memoria a utilizar es el Algoritmo con Compactacion.\n");
			if (algoritmoEleccion == ALGORITMO_WORST_FIT) {
				//printf("El algoritmo de Eleccion a utilizar es WORST FIT\n");
				int32_t espacioASetear = worstFit(diccionario.vectorCache, cantChunks);
				//printf("El espacio a setear es: [%d]\n", espacioASetear);
				if (espacioASetear == -1) {
					//printf(">> No hay espacio para almacenar\n");
					while ((vecesQueBorre < frecuenciaDeCompactacion) && (espacioASetear == -1)) {
						//printf("cantidad de eliminaciones realizadas: [%d] vez\n", vecesQueBorre);
						eliminarParticion(diccionario.vectorCache);
						vecesQueBorre++;
						espacioASetear = worstFit(diccionario.vectorCache, cantChunks);
					}
					if (espacioASetear == -1) {
						compactarCache(diccionario.cache, (diccionario.vectorCache));
						espacioASetear = worstFit(diccionario.vectorCache, cantChunks);
						if (espacioASetear == -1) {

							retorno = ENGINE_NOT_STORED;
						} else {
							itemAlocado->vectorCache = diccionario.vectorCache + espacioASetear;
							itemAlocado->ptrData = diccionario.cache + (espacioASetear * chunkSize);
							cargarKey(espacioASetear, cantChunks, nkey, (char*) key, diccionario, nbytes);
							retorno = ENGINE_SUCCESS;
						}
					} else {
						itemAlocado->vectorCache = diccionario.vectorCache + espacioASetear;
						itemAlocado->ptrData = diccionario.cache + (espacioASetear * chunkSize);
						cargarKey(espacioASetear, cantChunks, nkey, (char*) key, diccionario, nbytes);
						retorno = ENGINE_SUCCESS;
					}
				} else {
					itemAlocado->vectorCache = diccionario.vectorCache + espacioASetear;
					itemAlocado->ptrData = diccionario.cache + (espacioASetear * chunkSize);
					cargarKey(espacioASetear, cantChunks, nkey, (char*) key, diccionario, nbytes);
					retorno = ENGINE_SUCCESS;
				}
			} else if (algoritmoEleccion == ALGORITMO_NEXT_FIT) {
				//printf("Algoritmo de eleccion NEXT FITX\n");
				int32_t espacioASetear = NextFit(diccionario.vectorCache, cantChunks);
				//printf(">> Espacio a Setear: [%d]\n", (int32_t) espacioASetear);
				if (espacioASetear == -1) {
					while ((vecesQueBorre < frecuenciaDeCompactacion) && (espacioASetear == -1)) {
						//printf(">> Cantidad de eliminaciones realizadas: [%d]", vecesQueBorre);
						eliminarParticionNextFit(diccionario.vectorCache);
						vecesQueBorre++;
						espacioASetear = NextFit(diccionario.vectorCache, cantChunks);
					}
					if (espacioASetear == -1) {
						compactarCache(diccionario.cache, (diccionario.vectorCache));
						espacioASetear = NextFit(diccionario.vectorCache, cantChunks);
						if (espacioASetear == -1) {
							retorno = ENGINE_NOT_STORED;
						} else {
							itemAlocado->vectorCache = diccionario.vectorCache + espacioASetear;
							itemAlocado->ptrData = diccionario.cache + (espacioASetear * chunkSize);
							cargarKey(espacioASetear, cantChunks, nkey, (char*) key, diccionario, nbytes);
							retorno = ENGINE_SUCCESS;
						}

					} else {
						itemAlocado->vectorCache = diccionario.vectorCache + espacioASetear;
						itemAlocado->ptrData = diccionario.cache + (espacioASetear * chunkSize);
						cargarKey(espacioASetear, cantChunks, nkey, (char*) key, diccionario, nbytes);
						retorno = ENGINE_SUCCESS;
					}
				} else {
					itemAlocado->vectorCache = diccionario.vectorCache + espacioASetear;
					itemAlocado->ptrData = diccionario.cache + (espacioASetear * chunkSize);
					cargarKey(espacioASetear, cantChunks, nkey, (char*) key, diccionario, nbytes);
					retorno = ENGINE_SUCCESS;
				}
			}
		} else if (administracion == ADMINISTRACION_BUDDY) {
			//printf(">> Algoritmo de administración Buddy\n");
			int64_t espacioASetear = buddySystem(diccionario.vectorCache, nbytes);
			if (espacioASetear != -1) {
				//printf("Espacio a setear: [%d]\n", (uint32_t) espacioASetear);
				itemAlocado->vectorCache = diccionario.vectorCache + (uint32_t) espacioASetear;
				itemAlocado->ptrData = diccionario.cache + ((uint32_t) espacioASetear * chunkSize);
				cargarKey((uint32_t) espacioASetear, cantChunks, nkey, (char*) key, diccionario, nbytes);
				retorno = ENGINE_SUCCESS;
			} else {
				retorno = ENGINE_NOT_STORED;
				//printf("No se pudo almacenar el dato.\n");
			}
		}
	} else {
		//printf("El tamaño del dato es mayor que la Cache, no se almacenará.\n");
		retorno = ENGINE_E2BIG;
	}
	pthread_mutex_unlock(&mutexCache);
	//printf(">> Se desbloquea la cache.\n\n");
	return retorno;
}

t_CUCA_item* buscarYOcuparVectorItem() {
	uint8_t i;
	t_CUCA_item* retorno;
	pthread_mutex_lock(&mutexVectorItem);
	for (i = 0; i < cantidadConexiones && vectorItems[i].ocupado == 1; i++)
		;
	if (i < cantidadConexiones) {
		retorno = vectorItems + i;
		vectorItems[i].ocupado = 1;
	} else {
		retorno = NULL;
	}
	pthread_mutex_unlock(&mutexVectorItem);
	return retorno;
}

/*
 * Destruye un item allocado, esta es una función que tiene que utilizar internamente mecached
 */
static void CUCA_item_release(ENGINE_HANDLE *handler, const void *cookie, item* item) {
	//printf("CUCA_item_release.\n");
	pthread_mutex_lock(&mutexVectorItem);
	t_CUCA_item* itemLiberado = (t_CUCA_item*) item;
	loguearDatosTemplate("DEBUG", time(NULL), "RC", (uint32_t) getpid(), (uint32_t) pthread_self(), NIVEL_LOG_OBLIGATORIO, "Release - Key [%s]", itemLiberado->vectorCache->key);
	itemLiberado->ocupado = false;
	pthread_mutex_unlock(&mutexVectorItem);
	sem_post(&semaforoItems);
	t_CUCA_item *it = (t_CUCA_item*) item;
	uint32_t cantChunks;
	uint32_t reqSize = it->vectorCache->ndata;
	if (administracion == ADMINISTRACION_BUDDY) {
		if (!esPotencia(reqSize, 2)) {
			reqSize = potenciaDe2Proxima(reqSize);
		}
		if (reqSize < chunkSize)
			reqSize = chunkSize;
		cantChunks = reqSize / chunkSize;
	} else {
		cantChunks = reqSize / chunkSize;
		if (div(reqSize, chunkSize).rem != 0)
			cantChunks++;
	}
	uint32_t i;
	for (i = 0; i < cantChunks; i++) {
		it->vectorCache[i].enUso = 0;
		//printf("<CUCA_item_release> INTENTO DESBLOQUEAR CHUNK %d\n", i);
		//pthread_rwlock_unlock(&(it->vectorCache[i].semChunk));
		pthread_mutex_unlock(&(it->vectorCache[i].mutexChunk));
		//printf("<CUCA_item_release> DESBLOQUEÉ CHUNK %d\n", i);
	}

}

/*
 * Esta función lo que hace es mapear el item_info el cual es el tipo que memcached sabe manejar con el tipo de item
 * nuestro el cual es el que nosotros manejamos
 */
static bool CUCA_get_item_info(ENGINE_HANDLE *handler, const void *cookie, const item* item, item_info *item_info) {
	// casteamos de item*, el cual es la forma generica en la cual memcached trata a nuestro tipo de item, al tipo
	// correspondiente que nosotros utilizamos
	//printf("CUCA_get_item_info.\n");
	t_CUCA_item *it = (t_CUCA_item*) item;

	if (item_info->nvalue < 1) {
		return false;
	}

	item_info->cas = 0; /* Not supported */
	item_info->clsid = 0; /* Not supported */
	//item_info->exptime = it->vectorCache->exptime;
	//item_info->flags = it.vectorCache->flags;
	item_info->key = it->vectorCache->key;
	item_info->nkey = it->vectorCache->nkey;
	item_info->nbytes = it->vectorCache->ndata; /* Total length of the items data */
	item_info->nvalue = 1; /* Number of fragments used ( Default ) */
	item_info->value[0].iov_base = it->ptrData; /* Hacemos apuntar item_info al comienzo de la info */
	item_info->value[0].iov_len = it->vectorCache->ndata; /* Le seteamos al item_info el tamaño de la información */

	return true;
}

/*
 * Esta funcion es invocada cuando memcached recibe el comando get
 */
static ENGINE_ERROR_CODE CUCA_get(ENGINE_HANDLE *handle, const void* cookie, item** item, const void* key, const int nkey, uint16_t vbucket) {
	//printf(">>Inicio cuca get\n");
	ENGINE_ERROR_CODE retorno;
	pthread_mutex_lock(&mutexCache);
	loguearDatosTemplate("DEBUG", time(NULL), "RC", (uint32_t) getpid(), (uint32_t) pthread_self(), NIVEL_LOG_OBLIGATORIO, "Get - Key [%s]", key);
	//printf(">>Bloqueo el semáforo\n");
	//busco un valor por la key, si lo encuentro lo devuelvo.
	//caso contrario devuelvo codigo de error
	uint8_t encontrado = 0;
	uint32_t i = 0;
	uint32_t j = 0;
	while (!encontrado && (i < cacheSize / chunkSize)) {
		//printf(">>Itere [%d] veces\n", i);
		//printf("Key: [%s]\n", diccionario.vectorCache[i].key);
		//printf("Completo un ciclo while\n");
		if (compararKeys((char*) key, nkey,diccionario.vectorCache[i].key, diccionario.vectorCache[i].nkey)) {
			j = i;
			while ((j < cacheSize / chunkSize) && compararKeys(diccionario.vectorCache[j].key, diccionario.vectorCache[j].nkey, diccionario.vectorCache[i].key, diccionario.vectorCache[i].nkey)) {
				//printf("<CUCA_get> INTENTO BLOQUEAR CHUNK %d\n", j);
				//pthread_rwlock_rdlock(&(diccionario.vectorCache[i].semChunk));
				pthread_mutex_lock(&(diccionario.vectorCache[j].mutexChunk));
				//printf("<CUCA_get> BLOQUEÉ CHUNK %d\n", j);
				j++;
			}
			encontrado = 1;
			//printf("nData: [%d]\n", diccionario.vectorCache[i].ndata);
			//printf("nKey: [%d]\n", diccionario.vectorCache[i].nkey);
			//printf("Encontré la key: [%s] en la particion número [%d]\n", (char*) key, i);
		} else {
			//printf("La key buscada no se encuentra en esta partición\n");
			i++;
		}
	}
	if (encontrado) {
		//printf(">> Encontré el valor\n");
		struct timeval tiempo;
		gettimeofday(&tiempo, NULL);
		//printf("<CUCA_GET> segundos: %d\n<CUCA_GET> microsegundos: %d\n\n", (uint32_t) tiempo.tv_sec, (uint32_t) tiempo.tv_usec);
		for (j = i; (j < cacheSize / chunkSize) && compararKeys(diccionario.vectorCache[j].key, diccionario.vectorCache[j].nkey, diccionario.vectorCache[i].key, diccionario.vectorCache[i].nkey); j++) {
			if (algoritmoReemplazo == ALGORITMO_LRU) {
				diccionario.vectorCache[j].tiempo = tiempo;
			}
		}
		//t_CUCA_item *CUCA_item = malloc(sizeof(t_CUCA_item));
		sem_wait(&semaforoItems);
		t_CUCA_item *CUCA_item = buscarYOcuparVectorItem();
		//printf("tamaño a leer: [%d]\n", diccionario.vectorCache[i].ndata);
		//info = leerCache(diccionario.cache, i * chunkSize, diccionario.vectorCache[i].ndata);
		CUCA_item->vectorCache = diccionario.vectorCache + i;
		//CUCA_item->key = malloc(CUCA_item->nkey + 1);

		CUCA_item->ptrData = diccionario.cache + i * chunkSize;

		*item = (void*) CUCA_item;
		retorno = ENGINE_SUCCESS;
	} else {
		//printf(">>No encontré el valor\n");
		retorno = ENGINE_KEY_ENOENT;
	}
	pthread_mutex_unlock(&mutexCache);
	//printf("Desbloque el semáforo \n");
	return retorno;
}

/*
 * Esta función se llama cuando memcached recibe un set. La variable operation nos indica el tipo. Estos deben ser tratados indistintamente
 */
static ENGINE_ERROR_CODE CUCA_store(ENGINE_HANDLE *handle, const void *cookie, item* item, uint64_t *cas, ENGINE_STORE_OPERATION operation, uint16_t vbucket) {
	/* TODO que pasa si se intenta escribir un dato que ya se encuentra en la cache?????*/
	//printf("====================================================================================================\n");
	//printf(">> Comienza CUCA_store.\n");
	/*pthread_rwlock_wrlock(&semCache);
	 printf(">> Se bloquea la cache para escritura.\n\n");
	 t_CUCA_item *CUCA_item = (t_CUCA_item*) item;
	 if (!yaExisteKey(CUCA_item->key, CUCA_item->nkey, diccionario)) {
	 uint8_t vecesQueBorre = 0;
	 //almaceno un valor y lo asocio a su key
	 div_t division = div(CUCA_item->ndata, chunkSize);
	 uint32_t cantChunks = division.quot;
	 if (division.rem > 0) {
	 cantChunks++;
	 }
	 printf(">> Necesitamos [%d] chunks\n", cantChunks);
	 if (administracion == ADMINISTRACION_COMPACTACION) {
	 printf(">> El algoritmo de Administracion de Memoria a utilizar es el Algoritmo con Compactacion.\n");
	 if (algoritmoEleccion == ALGORITMO_WORST_FIT) {
	 printf("El algoritmo de Eleccion a utilizar es WORST FIT\n");
	 int32_t espacioASetear = worstFit(diccionario.vectorCache, cantChunks);
	 printf("El espacio a setear es: [%d]\n", espacioASetear);
	 if (espacioASetear == -1) {
	 printf(">> No hay espacio para almacenar\n");
	 while ((vecesQueBorre < frecuenciaDeCompactacion) && (espacioASetear == -1)) {
	 printf("cantidad de eliminaciones realizadas: [%d] vez\n", vecesQueBorre);
	 eliminarParticion(diccionario.vectorCache);
	 vecesQueBorre++;
	 espacioASetear = worstFit(diccionario.vectorCache, cantChunks);
	 }
	 if (espacioASetear == -1) {
	 compactarCache(diccionario.cache, (diccionario.vectorCache));
	 espacioASetear = worstFit(diccionario.vectorCache, cantChunks);
	 if (espacioASetear == -1) {

	 retorno = ENGINE_FAILED;
	 } else {
	 cargarCache(espacioASetear, cantChunks, CUCA_item->nkey, CUCA_item->key, diccionario, CUCA_item->data, CUCA_item->ndata);
	 retorno = ENGINE_SUCCESS;
	 }
	 } else {
	 cargarCache(espacioASetear, cantChunks, CUCA_item->nkey, CUCA_item->key, diccionario, CUCA_item->data, CUCA_item->ndata);
	 retorno = ENGINE_SUCCESS;
	 }
	 } else {
	 cargarCache(espacioASetear, cantChunks, CUCA_item->nkey, CUCA_item->key, diccionario, CUCA_item->data, CUCA_item->ndata);
	 retorno = ENGINE_SUCCESS;
	 }
	 } else if (algoritmoEleccion == ALGORITMO_NEXT_FIT) {
	 printf("Algoritmo de eleccion NEXT FITX\n");
	 int32_t espacioASetear = NextFit(diccionario.vectorCache, cantChunks);
	 printf(">> Espacio a Setear: [%d]\n", (int32_t) espacioASetear);
	 if (espacioASetear == -1) {
	 while ((vecesQueBorre < frecuenciaDeCompactacion) && (espacioASetear == -1)) {
	 printf(">> Cantidad de eliminaciones realizadas: [%d]", vecesQueBorre);
	 eliminarParticionNextFit(diccionario.vectorCache);
	 vecesQueBorre++;
	 espacioASetear = NextFit(diccionario.vectorCache, cantChunks);
	 }
	 if (espacioASetear == -1) {
	 compactarCache(diccionario.cache, (diccionario.vectorCache));
	 espacioASetear = NextFit(diccionario.vectorCache, cantChunks);
	 if (espacioASetear == -1) {
	 retorno = ENGINE_FAILED;
	 } else {
	 cargarCache(espacioASetear, cantChunks, CUCA_item->nkey, CUCA_item->key, diccionario, CUCA_item->data, CUCA_item->ndata);
	 retorno = ENGINE_SUCCESS;
	 }

	 } else {
	 cargarCache(espacioASetear, cantChunks, CUCA_item->nkey, CUCA_item->key, diccionario, CUCA_item->data, CUCA_item->ndata);
	 retorno = ENGINE_SUCCESS;
	 }
	 } else {
	 cargarCache(espacioASetear, cantChunks, CUCA_item->nkey, CUCA_item->key, diccionario, CUCA_item->data, CUCA_item->ndata);
	 retorno = ENGINE_SUCCESS;
	 }
	 }
	 } else if (administracion == ADMINISTRACION_BUDDY) {
	 printf(">> Algoritmo de administración Buddy\n");
	 int32_t espacioASetear = buddySystem(diccionario.vectorCache, CUCA_item->ndata);
	 printf("Espacio a setear: [%d]\n", espacioASetear);
	 cargarCache(espacioASetear, cantChunks, CUCA_item->nkey, CUCA_item->key, diccionario, CUCA_item->data, CUCA_item->ndata);
	 retorno = ENGINE_SUCCESS;
	 }
	 } else {
	 retorno = ENGINE_FAILED;
	 }
	 pthread_rwlock_unlock(&semCache);
	 printf(">> Se desbloquea la cache.\n\n");*/

	t_CUCA_item *it = (t_CUCA_item*) item;
	loguearDatosTemplate("DEBUG", time(NULL), "RC", (uint32_t) getpid(), (uint32_t) pthread_self(), NIVEL_LOG_OBLIGATORIO, "Store - Key [%s]", it->vectorCache->key);
	uint32_t cantChunks;
	uint32_t reqSize = it->vectorCache->ndata;
	if (administracion == ADMINISTRACION_BUDDY) {
		if (!esPotencia(reqSize, 2)) {
			reqSize = potenciaDe2Proxima(reqSize);
		}
		if (reqSize < chunkSize)
			reqSize = chunkSize;
		cantChunks = reqSize / chunkSize;
	} else {
		cantChunks = reqSize / chunkSize;
		if (div(reqSize, chunkSize).rem != 0)
			cantChunks++;
	}
	uint32_t i;
	for (i = 0; i < cantChunks; i++) {
		it->vectorCache[i].estado = 'O';
	}

	//printf(">> Finaliza CUCA_store.\n");
	//printf("====================================================================================================\n");
	return ENGINE_SUCCESS;
}

/*
 * Esta función se llama cuando memcached recibe un flush_all
 */
static ENGINE_ERROR_CODE CUCA_flush(ENGINE_HANDLE* handle, const void* cookie, time_t when) {
	//printf("inicio cuca flush\n");
	pthread_mutex_lock(&mutexCache);
	loguearDatos("DEBUG", time(NULL), "RC", (uint32_t) getpid(), (uint32_t) pthread_self(), "Flush", NIVEL_LOG_OBLIGATORIO);
	uint32_t i;
	for (i = 0; i < (cacheSize / chunkSize); i++) {
		//printf("<CUCA_flush> INTENTO BLOQUEAR CHUNK %d\n", i);
		//pthread_rwlock_wrlock(&(diccionario.vectorCache[i].semChunk));
		pthread_mutex_lock(&(diccionario.vectorCache[i].mutexChunk));
		//printf("<CUCA_flush> BLOQUEÈ CHUNK %d\n", i);
		diccionario.vectorCache[i].codBuddy = 0;
		diccionario.vectorCache[i].estado = 'L';
		diccionario.vectorCache[i].enUso = 0;
		memset(diccionario.vectorCache[i].key, '\0', 42);
		diccionario.vectorCache[i].tiempo.tv_sec = 0;
		diccionario.vectorCache[i].tiempo.tv_usec = 0;
		//printf("<CUCA_flush> INTENTO DESBLOQUEAR CHUNK %d\n", i);
		pthread_mutex_unlock(&(diccionario.vectorCache[i].mutexChunk));
		//pthread_rwlock_unlock(&(diccionario.vectorCache[i].semChunk));
		//printf("<CUCA_flush> DESBLOQUEÈ CHUNK %d\n", i);
	}
	pthread_mutex_unlock(&mutexCache);

	return ENGINE_SUCCESS;
}

/*
 * Esta función se llama cuando memcached recibe un delete
 */
static ENGINE_ERROR_CODE CUCA_item_delete(ENGINE_HANDLE* handle, const void* cookie, const void* key, const size_t nkey, uint64_t cas, uint16_t vbucket) {
	//printf("CUCA_item_delete. \n");
	pthread_mutex_lock(&mutexCache);
	loguearDatosTemplate("DEBUG", time(NULL), "RC", (uint32_t) getpid(), (uint32_t) pthread_self(), NIVEL_LOG_OBLIGATORIO, "Delete - Key [%s]", key);
	//busco un valor y lo elimino, sacando su key también
	ENGINE_ERROR_CODE retorno = ENGINE_SUCCESS;

	if (algoritmoEleccion == ALGORITMO_WORST_FIT) {
		retorno = buscaYBorraWorstFit(diccionario.vectorCache, (char*) key, nkey);
	} else {
		retorno = buscaYBorraNextFit(diccionario.vectorCache, (char*) key, nkey);
	}

	pthread_mutex_unlock(&mutexCache);

	return retorno;
}

/*
 * ************************************* Funciones Dummy *************************************
 */

static ENGINE_ERROR_CODE CUCA_get_stats(ENGINE_HANDLE* handle, const void* cookie, const char* stat_key, int nkey, ADD_STAT add_stat) {
	//printf("CUCA_get_stats.\n");
	return ENGINE_SUCCESS;
}

static void CUCA_reset_stats(ENGINE_HANDLE* handle, const void *cookie) {
	//printf("CUCA_reset_stats.\n");
}

static ENGINE_ERROR_CODE CUCA_unknown_command(ENGINE_HANDLE* handle, const void* cookie, protocol_binary_request_header *request, ADD_RESPONSE response) {
	//printf("CUCA_unknown_command.\n");
	return ENGINE_ENOTSUP;
}

static void CUCA_item_set_cas(ENGINE_HANDLE *handle, const void *cookie, item* item, uint64_t val) {
	//printf("CUCA_item_set_cas.\n");

}

/*
 * Handler de la SIGUSR1
 */
void CUCA_dummp(int signal) {
	pthread_mutex_lock(&mutexCache);
	iniciarInforme("../RC/dumpCache.info");
	informarCadena("====================================================================================================\n");
	informarCadena("==========================================INFORME DE CACHE==========================================\n");
	informarCadena("====================================================================================================\n");
	uint32_t i, cuentaParticiones = 0;
	uint32_t seguidosConMismoEstado;
	for (i = 0; i < cacheSize / chunkSize; cuentaParticiones++) {
		if (diccionario.vectorCache[i].estado == 'L') {
			if (administracion == ADMINISTRACION_COMPACTACION) {
				seguidosConMismoEstado = cantidadConMismoEstado(diccionario.vectorCache, &i, diccionario.vectorCache[i].estado, diccionario.vectorCache[i].enUso);
			} else {
				seguidosConMismoEstado = cantidadConMismoEstadoYCodBuddy(diccionario.vectorCache, &i, diccionario.vectorCache[i].estado, diccionario.vectorCache[i].codBuddy);
			}
		} else {
			if (administracion == ADMINISTRACION_COMPACTACION) {
				seguidosConMismoEstado = cantidadConMismoEstadoYKey(diccionario.vectorCache, &i, diccionario.vectorCache[i].estado, diccionario.vectorCache[i].key, diccionario.vectorCache[i].nkey);
				//printf("Seguidos: [%d]\n", seguidosConMismoEstado);
			} else {
				seguidosConMismoEstado = cantidadConMismoEstadoYCodBuddy(diccionario.vectorCache, &i, diccionario.vectorCache[i].estado, diccionario.vectorCache[i].codBuddy);
			}
		}
		informarTemplate("Particion %d: ", cuentaParticiones);
		//convertir a hexa
		informarTemplate("0x%x - ", (i - seguidosConMismoEstado) * chunkSize);
		informarTemplate("0x%x. ", (i * chunkSize) - 1);
		if (diccionario.vectorCache[i - seguidosConMismoEstado].estado == 'O') {
			informarCadena("Ocupada.");
		} else {
			informarCadena("Libre. ");
		}
		informarTemplate("Size: %d bytes.", seguidosConMismoEstado * chunkSize);
		if (diccionario.vectorCache[i - seguidosConMismoEstado].estado == 'O') {
			informarTemplate("Key: \"%s\" \n", diccionario.vectorCache[i - seguidosConMismoEstado].key);
		} else {
			informarCadena("\n");
		}
	}
	informarCadena("====================================================================================================\n");
	informarCadena("==========================================FIN DEL INFORME===========================================\n");
	informarCadena("====================================================================================================\n");
	cerrarInforme();
	pthread_mutex_unlock(&mutexCache);
}
